package cn.uncode.dal.core;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NamedThreadLocal;
import org.springframework.util.Assert;

import cn.uncode.dal.criteria.Model;

public class ThreadLocalCacheManager {
	

	private static final Logger LOG = LoggerFactory.getLogger(ThreadLocalCacheManager.class);
	
	public static boolean USE_TRANSACTION = false;

	private static final ThreadLocal<Map<String, Map<String, Object>>> DATA_SELECT =
			new NamedThreadLocal<Map<String, Map<String, Object>>>("Local select data");
	
	private static final ThreadLocal<Map<String, Model>> DATA_UPDATE =
			new NamedThreadLocal<Map<String, Model>>("Local update data");
	
	private static final ThreadLocal<Map<String, Model>> DATA_INSERT =
			new NamedThreadLocal<Map<String, Model>>("Local insert data");
	
	private static final ThreadLocal<Boolean> TRANSACTION =
			new NamedThreadLocal<Boolean>("Transaction status");
	
	private static BaseDAL baseDAL;
	
	public static void setBaseDAL(BaseDAL baseDAL){
		if(null == ThreadLocalCacheManager.baseDAL){
			ThreadLocalCacheManager.baseDAL = baseDAL;
		}
	}
	
	public static BaseDAL getBaseDAL(){
		return ThreadLocalCacheManager.baseDAL;
	}
	
	public static Map<String, Object> get(String key) {
		
		Map<String, Object> value = null;
		Map<String, Map<String, Object>> map = DATA_SELECT.get();
		if (null != map) {
			value = map.get(key);
		}
		
		if (value != null && LOG.isTraceEnabled()) {
			LOG.debug("Get value [" + value + "] for key [" + key + "] from thread [" +
					Thread.currentThread().getName() + "]");
		}
		return value;
	}
	
	public static void delect(String key){
		Map<String, Map<String, Object>> map = DATA_SELECT.get();
		if (null != map) {
			map.remove(key);
			if(LOG.isDebugEnabled()) {
				LOG.debug("Delete for key [" + key + "] from thread [" + Thread.currentThread().getName() + "]");
			}
		}
	}
	
	public static void executeTransaction(){
		if(null != TRANSACTION.get()){
			TRANSACTION.remove();
		}
		if(null != DATA_INSERT.get()){
			for(Model tb : DATA_INSERT.get().values()){
				baseDAL.insert(tb);
			}
			LOG.debug("Execute transaction insert value [" + DATA_INSERT.get().values() + "] for key [" + DATA_INSERT.get().keySet().toString() + "] to thread [" +
					Thread.currentThread().getName() + "]");
		}
		if(null != DATA_UPDATE.get()){
			for(Model tb : DATA_UPDATE.get().values()){
				baseDAL.updateByPrimaryKey(tb);
			}
			LOG.debug("Execute transaction update value [" + DATA_UPDATE.get().values() + "] for key [" + DATA_UPDATE.get().keySet().toString() + "] to thread [" +
					Thread.currentThread().getName() + "]");
		}
		DATA_INSERT.remove();
		DATA_UPDATE.remove();
	}


	public static void putSelect(String key, Map<String, Object> value){
		Assert.notNull(value, "Value must not be null");
		Map<String, Map<String, Object>> map = DATA_SELECT.get();
		if (map == null) {
			map = new HashMap<String, Map<String, Object>>();
			DATA_SELECT.set(map);
		}
		
		if(map.containsKey(key)){
			for(Entry<String, Object> item : value.entrySet()){
				map.get(key).put(item.getKey(), item.getValue());
			}
		}else{
			map.put(key, value);
		}
		
		map.put(key, value);
		if (LOG.isDebugEnabled()) {
			LOG.debug("Put select value [" + value + "] for key [" + key + "] to thread [" +
					Thread.currentThread().getName() + "]");
		}
	}
	
	public static void putSelect(Model model){
		String pkString = getPrimaryKey(model);
		if(StringUtils.isNotBlank(pkString)){
	        putSelect(pkString, model.getContent());
		}
	}
	
	private static String getPrimaryKey(Model model){
		String cacheKey = model.getTableName() + AbstractBaseDAL.CACHE_KEY_SELECT_BY_PRIMARY_KEY + model.getSinglePrimaryKey();
        if (StringUtils.isNotBlank(model.getDatabase())) {
            cacheKey = AbstractBaseDAL.CACHE_KEY_PREFIX + model.getDatabase() + "#" + cacheKey;
        } else {
            cacheKey = AbstractBaseDAL.CACHE_KEY_PREFIX + cacheKey;
        }
		return cacheKey;
	}
	
	public static boolean putInsert(Model model){
		Assert.notNull(model, "Value must not be null");
		if(!USE_TRANSACTION){
			return false;
		}
		if(null == baseDAL){
			return false;
		}
		
		Map<String, Model> map = DATA_INSERT.get();
		if (map == null) {
			map = new HashMap<String, Model>();
			DATA_INSERT.set(map);
		}

		String pkString = getPrimaryKey(model);
		if(StringUtils.isNotBlank(pkString)){
			if (LOG.isDebugEnabled()) {
				LOG.debug("Put insert value [" + model + "] for key [" + pkString + "] to thread [" +
						Thread.currentThread().getName() + "]");
			}
			map.put(pkString, model);
			putSelect(model);
			return true;
		}
		return false;
	}
	
	public static boolean putUpdate(Model model){
		Assert.notNull(model, "Value must not be null");
		boolean rt = true;
		if(!USE_TRANSACTION){
			rt = false;
		}
		if(null == baseDAL){
			rt = false;
		}
		
		String pkString = getPrimaryKey(model);
		if(StringUtils.isNotBlank(pkString)){
			if(null != DATA_INSERT.get() && DATA_INSERT.get().containsKey(pkString)){
				Model tb = DATA_INSERT.get().get(pkString);
				 for(Entry<String, Object> item : model.getContent().entrySet()){
					 tb.getContent().put(item.getKey(), item.getValue());
				 }
				 if (LOG.isDebugEnabled()) {
						LOG.debug("Put udpate->insert value [" + model + "] for key [" + pkString + "] to thread [" +
								Thread.currentThread().getName() + "]");
				 }
			}else{
				Map<String, Model> map = DATA_UPDATE.get();
				if (map == null) {
					map = new HashMap<String, Model>();
					DATA_UPDATE.set(map);
				}
				if(map.containsKey(pkString)){
					for(Entry<String, Object> item : model.getContent().entrySet()){
						map.get(pkString).getContent().put(item.getKey(), item.getValue());
					}
				}else{
					map.put(pkString, model);
				}
				if (LOG.isDebugEnabled()) {
					LOG.debug("Put update value [" + model + "] for key [" + pkString + "] to thread [" +
							Thread.currentThread().getName() + "]");
				}
			}
			putSelect(model);
			rt = false;
		}
		return rt;
	}
	
	
	public static void begin() {
		Boolean rt = TRANSACTION.get();
		if (rt == null) {
			TRANSACTION.set(Boolean.TRUE);
			if (LOG.isDebugEnabled()) {
				LOG.debug("Transaction begin to thread [" + Thread.currentThread().getName() + "]");
			}
		}
	}
	
	public static boolean isTransaction() {
		Boolean rt = TRANSACTION.get();
		if (rt == null) {
			return false;
		}
		return rt;
	}
	

	
	public static void clearTransaction() {
		DATA_UPDATE.remove();
		DATA_INSERT.remove();
		TRANSACTION.remove();
		if (LOG.isDebugEnabled()) {
			LOG.debug("Transaction clear to thread [" + Thread.currentThread().getName() + "]");
		}
	}


	public static void clear() {
		DATA_SELECT.remove();
		DATA_UPDATE.remove();
		DATA_INSERT.remove();
		TRANSACTION.remove();
	}



}
